/*****************************************************************************\

Microsoft Research Asia
Copyright (c) Microsoft Corporation. All Rights Reserved.

Module Name:
  ItgImage.cpp

Notes:
  This module is designed for process of Integral Image.

History:
  Dec 18, 2002 by Rong Xiao
  Modified on Jun/25/2004 by i-tabao@microsoft.com
     code clean

\*****************************************************************************/
#include "stdafx.h"
#include "CIntegralImage.h"
#include "DetConfig.h"

//===========================================================================
//    Destructor 
//
//    Parameters
//        None
//
//    Return Values
//        None
//===========================================================================
CIntegralImageBase::~CIntegralImageBase()
{
    ReleaseMemory();
}


//===========================================================================
//    AllocMemory
//        Allocate nesessary memory
//
//    Parameters
//        nWidth
//          Width of integral image
//        nHeight
//          Height of integral image
//
//    Return Values
//        S_OK indicates success. Otherwise for failure
//===========================================================================
HRESULT CIntegralImageBase::AllocMemory(unsigned uWidth, unsigned uHeight)
{
    HRESULT hr = S_OK;

    if ((uWidth < 0) || (uHeight < 0) || ((uWidth + 1) * (uHeight + 1) > MAXBUFFERSIZE))
    {
        hr = E_INVALIDARG;
    }

	//allocate memory if required
    if ( SUCCEEDED(hr) && uWidth*uHeight > m_uMemoryAllocated )
    {
		ReleaseMemory();
		int* piBuffer = new int[(uWidth + 1) * (uHeight + 1)];
		if (!piBuffer)
		{
			hr = E_OUTOFMEMORY;        
		}
		else
		{
			m_piItgImage=piBuffer;
			m_uMemoryAllocated =  uWidth*uHeight;
		}
	}
	//Initialize memory
	if (SUCCEEDED(hr))
	{
		m_uWidth = uWidth;
		m_uHeight = uHeight;
        ZeroMemory(m_piItgImage, sizeof(int) * (uWidth + 1) * (uHeight + 1));
    }
    return hr;
}


//===========================================================================
//    ReleaseMemory
//        Free all memory
//
//    Parameters
//        None
//
//    Return Values
//        None
//===========================================================================
void CIntegralImageBase::ReleaseMemory()
{
    if (m_piItgImage)
    {
        delete[] m_piItgImage;
        m_piItgImage = NULL;
		m_uMemoryAllocated = 0;
    }
}

//===========================================================================
//    CalCIntegralImage
//        Calculate integral image of the input image
//	  Algorithm description
//			Initialize ii(-1,.) = 0, ii(.,-1)=0
//			for (x=0; x< width ; x++)
//				for (y=0; y< height; y++ )
//					s(x,y) = s(x,y-1) + i(x,y)
//					ii(x,y) = ii(x-1,y) + s(x,y)
//    Parameters     
//        pBuf            Input grayscale image buffer
//        uSrcWidth, uSrcHeight		The width/height of the input image
//
//    Return Values
//        S_OK indicates success. Otherwise for failure
//===========================================================================
HRESULT CIntegralImageBase::CalCIntegralImage(const BYTE* pBuf, unsigned uSrcWidth, unsigned uSrcHeight)
{
	HRESULT hr;

	//m_uWidth, m_uHeight are equal to the desired image size after allocation successfully
	hr = AllocMemory(uSrcWidth, uSrcHeight);

	if (SUCCEEDED(hr))
	{
		unsigned uIntegralStride = m_uWidth + 1;
		unsigned uPos;
		for (unsigned x = 0; x< m_uWidth; x++)
		{
			uPos = x;
			unsigned index_xy = x+1;			//PointTo ii(x,-1)
			int iSumCol=0;		// s(x,-1)=0
			for (unsigned y = 0; y < m_uHeight; y++, uPos+=m_uWidth)
			{
				iSumCol += pBuf[uPos];					//calculate s(x,y)
				index_xy += uIntegralStride;		//Point to ii(x,y)
				m_piItgImage[index_xy] = m_piItgImage[index_xy-1] + iSumCol;
			}
		}
	}
	return hr;
}

//===========================================================================
//   AutoContrast
//        Calculate auto contrast factor for Haar-like feature
//
//    Parameters     
//        piGrayHist:	gray histogram
//
//    Return Values
//        S_OK indicates success. Otherwise for failure
//===========================================================================
HRESULT CIntegralImage::AutoContrast(const unsigned int* puGrayHist)
{
	HRESULT hr=S_OK;
	unsigned int puBuffer[257], *puGrayIntegralHist=puBuffer+1;
	if ( !puGrayHist ) {
		hr = E_INVALIDARG;
	}
	else {
		//Build integral histogram
		puGrayIntegralHist[-1]=0;
		for ( unsigned i=0; i<256; i++ ) {
			puGrayIntegralHist[i] = puGrayIntegralHist[i-1] + puGrayHist[i];
		}
		unsigned int uHistFocusSum=(unsigned int)(puGrayIntegralHist[255]*m_flAutoContrastFactor+0.5f);
		// Find the shortest range;
		int iShortestRange=255;
		int BestLeft, BestRight;
		int  iLeft, iRight;
		for ( iRight=255; puGrayIntegralHist[iRight]>=uHistFocusSum; iRight--) {
			for (iLeft=iRight; iLeft>=-1; iLeft--) {
				if (puGrayIntegralHist[iRight] >= puGrayIntegralHist[iLeft]+uHistFocusSum) {
					break;
				}
			}
			if ( puGrayIntegralHist[iRight] >= puGrayIntegralHist[iLeft]+uHistFocusSum && iRight - iLeft < iShortestRange) {
				iShortestRange = iRight -iLeft;
				BestLeft = iLeft;
				BestRight = iRight;
			}
		}
		m_flAutoContrast = 256.0f/(iShortestRange+1);
	}
	//we just use the function version with nDstWidth and nDstHeight to deal this case
	return hr;
}


HRESULT CIntegralImageBase::CalCIntegralImage(const CImageWrapper* pImage)
{
	CImagePos imgPos;
	imgPos.Init(pImage);
	return CalCIntegralImage(pImage, imgPos);
}

HRESULT CIntegralImageBase::CalCIntegralImage(const CImageWrapper* pImage, unsigned uDstWidth, unsigned uDstHeight)
{
	CScaledImagePos imgPos;
	imgPos.Init(pImage,uDstWidth,uDstHeight);
	return CalCIntegralImage(pImage, imgPos);
}

HRESULT CIntegralImageBase::CalCIntegralImage(const CImageWrapper* pImage, unsigned uDstWidth, unsigned uDstHeight, double dAngle)
{
	CRotatedImagePos imgPos;
	imgPos.Init(pImage,uDstWidth,uDstHeight,dAngle);
	return CalCIntegralImage(pImage, imgPos);
}

